No description has been provided for this image

M2.911 · Aprenentatge automàtic · PAC4

2023-2 · Màster universitari en Ciència de dades (Data science)

Estudis d'informàtica, multimèdia i telecomunicació

 
Nom i cognoms:

PAC 4: Combinació de models de classificació¶

Objectiu:¶

Desenvolupar una eina per a l'ajuda a la presa de decisions al diagnòstic d'Insuficiència Cardíaca (IC) utilitzant models predictius. Aquest treball requerirà que els estudiants apliquin tècniques ja desenvolupades en treballs anteriors de preprocessament, equilibri del conjunt de dades i apliquin els conceptes sobre els mètodes de conjunt per a la classificació.

Requisits:¶

Exploració i preprocessament de dades:

  • Realitzeu una anàlisi de dades exploratòria per comprendre les característiques del conjunt de dades, inclosa la verificació del desequilibri de classes.

Reviseu si cal netejar i preprocessar les dades, inclòs el maneig de valors faltants i codificació categòrica.

Anàlisi estadística per identificar el desequilibri:¶

  • Utilitzeu mètodes estadístics per confirmar la presència de desequilibri de classes.

Aplicar tècniques apropiades apreses en cursos anteriors per abordar el desequilibri.

Divisió i estandardització de dades:¶

  • Dividiu les dades en conjunts d'entrenament i prova. Després estandarditzeu les característiques per garantir que el rendiment del model no estigui esbiaixat per l'escala de les dades.

Construcció de models i ajustament d'hiperparàmetres:¶

  • Implementeu diversos classificadors i combineu-los utilitzant tècniques de conjunt.

Optimitzeu els classificadors utilitzant la cerca amb validació creuada per ajustar els hiperparàmetres.

Avaluació del model:¶

  • Avalueu el rendiment del model utilitzant mètriques adequades (per exemple, precisió, ROC AUC).

Incloeu una avaluació detallada que compari l'exercici de classificadors individuals i l'enfocament conjunt.

Aquesta activitat està dividida en quatre parts:

  • 1. Càrrega i ajustament de la base de dades MI - 1.5 ptos: Realitzeu els procediments estadístics necessaris que considereu necessaris per entendre la base de dades i verificació de l'equilibri de classes.
  • 2.1 Combinació paral·lela de classificadors - 2.5 punts: Estudi de diferents mètodes de combinació de classificadors en paral·lel: arbres de decisió, Bagging, and Boosting. 2.2 Combinació seqüencial de classificadors - 2.5 punts : Estudi de diferents mètodes de combinació de classificadors en seqüència: KNN, SVM, Stacking, i Cascading.
  • 3.1 Xarxes nueronals seqüencial i 3.2 paral·lela - 2.5 ptos: Construcció de dues xarxes nueronals amb diferents topologies sèrie i paral·lel.
  • 4. Anàlisi i Conclusió - 1 pt: Aquest treball et desafia a utilitzar totes les habilitats que has adquirit en el curs, enfocant-se a crear un model predictiu que no només calgui sinó també interpretable i validat estadísticament. Això et prepararà per a desafiaments de la ciència de dades del món real, on comprendre que el comportament del model i garantir-ne la validesa és tan crucial com aconseguir una alta precisió.

Consideracions generals:¶

  • La solució plantejada no pot utilitzar mètodes, funcions o paràmetres declarats deprecated en futures versions, llevat que s'indiqui explícitament.
  • Aquesta PAC s'ha de fer de manera estrictament individual. Qualsevol indici de còpia serà penalitzat amb un suspens (D) per a totes les parts implicades i la possible avaluació negativa de lassignatura de forma íntegra.
  • Cal que l'estudiant indiqui totes les fonts que ha fet servir per a la realització de la PAC. Si no és així, es considerarà que l'estudiant ha comès plagi, i és penalitzat amb un suspens (D) i la possible avaluació negativa de l'assignatura de forma íntegra.
  • En aquesta PAC cal utilitzar únicament les llibreries que s'importen a continuació. Si voleu utilitzar una altra llibreria cal consultar-ho amb el tutor/tutora de l'aula.

Format del lliurament:¶

  • Alguns exercicis poden suposar diversos minuts d'execució, per la qual cosa el lliurament s'ha de fer a format notebook ia format html, on es vegi el codi, els resultats i comentaris de cada exercici. Es pot exportar el notebook a HTML des del menú File $\to$ Download as $\to$ HTML. PER FAVOR NO PUJAR UN NOMÉS ARXIU COMPRIMIT.
  • El nom dels fitxers ha de tenir el seu NOM_COGNOM.
  • Hi ha un tipus de cel·la especial per albergar text. Aquest tipus de cel·la us serà molt útil per respondre les diferents preguntes teòriques plantejades al llarg de lactivitat. Per canviar el tipus de cel·la a aquest tipus, al menú: Cell $\to$ Cell Type $\to$ Markdown.

Infart de Miocardi (IM):¶

L'Infart de Miocardi (IM) és un dels problemes més desafiadors de la medicina moderna. L'infart agut de miocardi s'associa amb una alta mortalitat el primer any posterior. La incidència d'IM continua sent alta a tots els països. Això és especialment cert per a la població urbana de països altament desenvolupats, exposada a factors d'estrès crònic, nutrició irregular i no sempre equilibrada. El curs de la malaltia en pacients amb infart de miocardi és diferent. L'IM pot passar sense complicacions o amb complicacions que no empitjoren el pronòstic a llarg termini. Alhora, aproximadament la meitat dels pacients en els períodes agut i subagut tenen complicacions que condueixen a l'empitjorament de la malaltia i fins i tot a la mort. Fins i tot, un especialista experimentat no sempre pot preveure el desenvolupament d'aquestes complicacions. En aquest sentit, la predicció de les complicacions de l'infart de miocardi per fer oportunament les mesures preventives necessàries és una tasca important.

Iniciem la PAC amb la càrrega de les llibreries següents:

In [323]:
# pip install imbalanced-learn
Requirement already satisfied: imbalanced-learn in /Users/ulises.rey/opt/anaconda3/envs/openCV_local/lib/python3.8/site-packages (0.12.3)
Requirement already satisfied: numpy>=1.17.3 in /Users/ulises.rey/opt/anaconda3/envs/openCV_local/lib/python3.8/site-packages (from imbalanced-learn) (1.23.5)
Requirement already satisfied: scipy>=1.5.0 in /Users/ulises.rey/opt/anaconda3/envs/openCV_local/lib/python3.8/site-packages (from imbalanced-learn) (1.10.1)
Requirement already satisfied: scikit-learn>=1.0.2 in /Users/ulises.rey/opt/anaconda3/envs/openCV_local/lib/python3.8/site-packages (from imbalanced-learn) (1.2.2)
Requirement already satisfied: joblib>=1.1.1 in /Users/ulises.rey/opt/anaconda3/envs/openCV_local/lib/python3.8/site-packages (from imbalanced-learn) (1.2.0)
Requirement already satisfied: threadpoolctl>=2.0.0 in /Users/ulises.rey/opt/anaconda3/envs/openCV_local/lib/python3.8/site-packages (from imbalanced-learn) (2.2.0)
Note: you may need to restart the kernel to use updated packages.
In [325]:
from matplotlib import pyplot as plt
import pandas as pd
import numpy as np
import scipy.stats
from sklearn.linear_model import LinearRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score
from sklearn.metrics import accuracy_score
from sklearn import ensemble
from sklearn import svm
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
import seaborn as sns
from matplotlib import pyplot as plt
from imblearn.over_sampling import SMOTE



%matplotlib inline

Ruta de treball a Colab¶

Les línies de codi següents són d'ajuda en cas que vulgueu treballar a Colab. Recordeu que és opcional. Els punts successius a la línia de codi:

os.listdir('/content/drive/MyDrive/...')

han de reemplaçar-se per la ruta, al vostre ordinador que contingui els documents per realitzar aquesta PAC.

In [ ]:
from google.colab import drive
drive.mount('/content/drive')
In [ ]:
import os
os.listdir('/content/drive/My Drive/...')
In [ ]:
import os
os.chdir('/content/drive/My Drive/...')

1. Càrrega i ajustament de la base de dades MI (1.5 punts)¶

Feu una anàlisi exploratòri a les dades per comprendre'n les seves característiques, inclosa la verificació del desequilibri de classes. Netegeu i preprocesseu les dades, analitzeu si hi ha valors faltants i realitzeu la codificació categòrica si és necessari.

1.1 Dades¶

La base de dades original consta de 1700 pacients amb 124 variables registrades. Hi ha valors faltants i outlayers. En aquesta base de dades original, les columnes 2-112 són els descriptors o variables per predir possibles complicacions o respostes que són a les columnes 113-124. La descripció detallada i les dades estan disponibles al següent enllaç.

És una base de dades ideal per aplicar els conceptes analitzats durant el curs. Descarrega la base de dades anomenada MI_database_final.csv. Els valors faltants ja estan imputats i els outlayers han estat eliminats i igualment imputats. A més, les variables han estat reduïdes a les més rellevants tenint en compte que l'outcome és ZSN_A que és una variable categòrica i el signnificat de la qual és la presència d'Insuficiència Cardíaca (IC) crònica a l'anamnesi.

En aquestes dades l'outcome és ZSN_A conté cinc resultats diferents o gravetat de l'afecció:

  1. Classe 0: Aquesta classe és la més comuna i representa pacients sense complicacions o amb complicacions lleus després d'un infart de miocardi. Això podria incloure pacients que hagin rebut un tractament immediat exitós o aquells amb símptomes inicials menys greus.

  2. Classe 1: Podeu indicar pacients que tenen complicacions moderades, que possiblement requereixin intervencions mèdiques addicionals, però no necessàriament indica un mal pronòstic a llarg termini.

  3. Classe 2: Pot representar complicacions més greus que requereixin una intervenció mèdica més gran i podrien associar-se amb un pitjor pronòstic que les classes 0 o 1.

  4. Classe 3: aquesta classe, amb relativament pocs casos, pot denotar complicacions crítiques i potencialment mortals que requereixen cures intensives.

  5. Classe 4: El menys comú, que possiblement representi resultats fatals o pacients amb complicacions extremadament greus que condueixin a altres falles sistèmiques.

Per iniciar qualsevol anàlisi, el primer que cal fer és lanàlisi univariable, diagrames de distribució utilitzant histogrames o diagrames de barres dacord amb la naturalesa de la variable. Per identificar els valors extrems, realitzeu diagrames de caixa o box plot. En cas que existeixin, aquests han de ser eliminats i es consideraran com a faltants. En cas de necessitat d'ajustar un nou valor calculeu-lo utilitzant la regressió lineal.

A cadascuna de les seccions de l'anàlisi estadística estan suggerides algunes variables, però recordeu que sou experts i heu d'ampliar l'estudi si ho veieu necessari.

Per començar, carregueu el dataset.

In [326]:
df = pd.read_csv('data/MI_database_final.csv')
In [327]:
nRow, nCol = df.shape
print(f'Hay {nRow} filas y {nCol} columnas')
df.head()
Hay 1700 filas y 104 columnas
Out[327]:
ID L_BLOOD AGE ALT_BLOOD K_BLOOD ROE S_AD_ORIT AST_BLOOD Na_BLOOD TIME_B_S ... GIPER_Na ritm_ecg_p_04 fibr_ter_03 GT_POST fibr_ter_06 np09 zab_leg_04 n_r_ecg_p_02 n_p_ecg_p_05 ZSN_A
0 1 8.0 77.0 0.38 4.7 16.0 180.0 0.22 138.0 4.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
1 2 7.8 55.0 0.38 3.5 3.0 120.0 0.18 132.0 2.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
2 3 10.8 52.0 0.30 4.0 10.0 180.0 0.11 132.0 3.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
3 4 8.0 68.0 0.75 3.9 10.0 120.0 0.37 146.0 2.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0
4 5 8.3 60.0 0.45 3.5 10.0 160.0 0.22 132.0 9.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0

5 rows × 104 columns

Inicialitzar la seed

In [328]:
myseed = 13

1.2 Anàlisi descriptiva univariable¶

L'estadística descriptiva és la disciplina de l'estadística que s'encarrega d'organitzar i resumir informació quantificativa per a descriure les característiques principals d'un conjunt de dades. Frecuentemente, el conjunt de dades inclou diferents variables (per exemple: velocitat, resistència, elasticitat, etc). Per lo tant, lo més usual és considerar, en un principi, les variables d'una en una, sense tenir possible relació entre elles. Segons les característiques d'aquestes variables, es poden trobar variables qualitatives o categóricas (NO necessiten números per expressar-se, per exemple: sexo, color, etc.) i variables quantitatives o numèriques* * (SI necessitan números per expressar-se, per exemple: edat, longitud, etc). Per a cada variable hi ha una sèrie d'observacions, les anotacions sobre quina modalitat (cualitatives) o quin valor (cuantitatives) té cada observació que es denomina datos**. Aquestes dades es poden organitzar, resumir i representar mitjançant: Taules, mesures (estadístics) i gràfiques.

Taula de freqüència¶

El modo más simple de presentar ordenadamente datos categóricos es por medio de una tabla de frecuencias. Aquesta taula indica el nombre de repeticions de cada una de les classes de la variable qualitativa. Podeu distingir els següents tipus de freqüències:

• Freqüència absoluta $(n_{i})$: És el nombre de repeticions que presenta una observació.

• Frecuència relativa $(f_{i})$: És la freqüència absoluta dividida pel nombre total de dades.

• Freqüència absoluta acumulada $(N_{i})$: És la suma dels diferents valors de la freqüència absoluta prenent com a referència un individu donat.

• Frecuència relativa acumulada $(F_{i})$: És el resultat de dividir cada freqüència absoluta acumulada pel nombre total de dades.

Divida los datos en dos partes, X para todas las variables o características que no son el outcome i y para el outcome.

In [329]:
df.columns
Out[329]:
Index(['ID', 'L_BLOOD', 'AGE', 'ALT_BLOOD', 'K_BLOOD', 'ROE', 'S_AD_ORIT',
       'AST_BLOOD', 'Na_BLOOD', 'TIME_B_S',
       ...
       'GIPER_Na', 'ritm_ecg_p_04', 'fibr_ter_03', 'GT_POST', 'fibr_ter_06',
       'np09', 'zab_leg_04', 'n_r_ecg_p_02', 'n_p_ecg_p_05', 'ZSN_A'],
      dtype='object', length=104)
In [330]:
X = df.drop("ZSN_A", axis=1)
y = df["ZSN_A"]

Calculeu les freqüències per a l'outcome ZSN_A. Recordeu, que a tota la secció de l'anàlisi estadística hi ha una guia d'estudi per a determinades variables. En cas que considereu necessari estendre aquesta anàlisi a altres variables, es pot fer. Aquí és el moment de demostrar les seves capacitats i aptituds adquirides a l'anàlisi de dades.

In [331]:
# check basic info
y.describe(), y.info()
<class 'pandas.core.series.Series'>
RangeIndex: 1700 entries, 0 to 1699
Series name: ZSN_A
Non-Null Count  Dtype  
--------------  -----  
1700 non-null   float64
dtypes: float64(1)
memory usage: 13.4 KB
Out[331]:
(count    1700.000000
 mean        0.188235
 std         0.649065
 min         0.000000
 25%         0.000000
 50%         0.000000
 75%         0.000000
 max         4.000000
 Name: ZSN_A, dtype: float64,
 None)
In [332]:
# Absolute frequency
abs_freq = y.value_counts()
abs_freq
Out[332]:
0.0    1522
1.0     103
3.0      29
2.0      27
4.0      19
Name: ZSN_A, dtype: int64
In [333]:
# Relative frequency
rel_freq = y.value_counts(normalize=True)
rel_freq
Out[333]:
0.0    0.895294
1.0    0.060588
3.0    0.017059
2.0    0.015882
4.0    0.011176
Name: ZSN_A, dtype: float64
In [334]:
# Accumulated absolute frequency
acc_abs_freq = y.value_counts().cumsum()
acc_abs_freq
Out[334]:
0.0    1522
1.0    1625
3.0    1654
2.0    1681
4.0    1700
Name: ZSN_A, dtype: int64
In [335]:
# Accumulated relative frequency
y.value_counts(normalize=True).cumsum()
Out[335]:
0.0    0.895294
1.0    0.955882
3.0    0.972941
2.0    0.988824
4.0    1.000000
Name: ZSN_A, dtype: float64

1.3 Mesures de tendència central: mitjana, mediana i moda¶

De vegades és convenient resumir la informació d'un conjunt de dades numèriques en un sol valor per obtenir indicadors del comportament de la variable i poder fer comparacions. Les mesures de tendència central, també conegudes com a mesures de posició o localització, descriuen un valor al voltant del qual es troben les observacions.

Mitjana: També coneguda com a valor mitjà, es defineix com la suma de tots els valors de cada observació $(x_{i})$ dividit pel nombre total d'observacions del conjunt de dades ($N$).

$$\bar{X}=\frac{x_{1}+x_{2}+x_{3}+x_{4}+\cdots+x_{N}}{N}=\frac{1}{N }\sum_{i=1}^{N}x_{i}$$

Mediana: És la dada que ocupa la posició central a la mostra ordenada de menor a major, és un punt que divideix la mostra ordenada en dos grups iguals (deixa el 50% dels valors per sota i l'altre 50% per sobre). Per calcular-la s'ordenen les dades de menor a major, la dada central és la que ocupa la posició $\dfrac{N+1}{2}$ on $N$ és el nombre total de dades. Si $N$ és imparell, la mediana és la mateixa dada central, si $N$ és parell, hi ha dues dades centrals, per tant la mediana és la mitjana d'aquests dos.

Moda: És el valor amb més freqüència absoluta en les dades obtingudes, indica quin és el valor més freqüent però no quantes vegades es repeteix. Si hi ha més de dos valors que es repeteixen amb més freqüència, es diu que les dades són multimodals.

Per calcular aquestes mesures, simplement seleccionem la variable estadística del DataFrame i fem servir els mètodes mean, median i mode respectivament.

Calculeu aquestes mesures per a la variable AGE i K_BLOOD.

In [336]:
df[["AGE", "K_BLOOD"]].mean()
Out[336]:
AGE        61.862353
K_BLOOD     4.171471
dtype: float64
In [337]:
df[["AGE", "K_BLOOD"]].median()
Out[337]:
AGE        63.0
K_BLOOD     4.1
dtype: float64
In [338]:
df[["AGE", "K_BLOOD"]].mode()
Out[338]:
AGE K_BLOOD
0 63.0 4.1

1.4 Mesures de posició: quartils, decils i percentils¶

Els quantils són valors de la llista de dades que la divideixen en parts iguals, és a dir, en intervals que comprenen el mateix nombre de valors. Els més usats són els percentils, els decils i els quartils. Els percentils són 99 valors que divideixen en cent parts iguals el conjunt de dades ordenades. Per exemple, el percentil d‟ordre 15 deixa per sota el 15% de les observacions, i per sobre queda el 85%. Els decils són els nou valors que divideixen el conjunt de dades ordenades en deu parts iguals, són un cas particular dels percentils. Els quartils són els tres valors que divideixen el conjunt de dades ordenades en quatre parts iguals, són també un cas particular dels percentils. A Python, qualsevol d'aquests es calcula amb el mètode quantile, on addicionalment s'ha d'especificar el quantil o quantils desitjats (com un valor entre 0 i 1).

Calculeu aquestes mesures per a la variable L_BLOOD.

In [339]:
df["L_BLOOD"].quantile([0.15, 0.85])
Out[339]:
0.15     5.7
0.85    11.9
Name: L_BLOOD, dtype: float64
In [340]:
df["L_BLOOD"].quantile([0.25, 0.5, 0.75, 1])
Out[340]:
0.25     6.6
0.50     8.0
0.75    10.2
1.00    27.9
Name: L_BLOOD, dtype: float64

1.5 Mesures de dispersió: desviació típica, rang, IQR, variància¶

Les mesures de posició donen una idea d'on es troba el centre de la distribució, però no ens diuen que tant dispers és el conjunt de dades. Les mesures de dispersió o variabilitat descriuen que tan a prop hi ha les dades entre elles, o d'alguna mesura de tendència central.

Rang: És l'interval entre el valor màxim i el valor mínim del conjunt de dades. És altament sensible als valors extrems, és a dir, és un paràmetre estadístic feble.

Rang interquartílic: És l'extensió coberta per la meitat central de les dades ordenades, excloent-ne la quarta part inicial (les que són inferiors al primer quartil) i la quarta part final (les que són superiors al tercer quartil).

Variança i desviació típica: Aquestes mesures mesuren com de lluny difereixen les dades de la mitjana. Específicament, expressen "la mitjana de la distància de cada punt respecte de la mitjana". La variància es calcula segons: $$\sigma^{2}=\frac{1}{N}\sum_{i=1}^{N}(x_{i}-\bar{X})^{2},$$ on $x_{i}$ és el valor de cada observació, $\overline{X}$ és la mitjana i $N$ és el nombre total de dades. Noteu que les unitats de la variància estan expressades al quadrat, per tant, si es tenen dades de longitud (en $mm$), la variància resulta amb unitats de superfície (en $mm^{2}$), la qual cosa no té molt de sentit. Per tant, es disposa de la desviació estàndard o típica que no és més que l'arrel quadrada de la variància; així, les unitats de la mesura de dispersió són les mateixes de les dades.

$$\sigma=\sqrt{\sigma^{2}}=\sqrt{\frac{1}{N}\sum_{i=1}^{N}\left(x_{i}-\bar{X }\right)^{2}}$$

A pandes la desviació estàndard i la variància es poden calcular mitjançant els mètodes std(ddof=1) i var(ddof=1) respectivament, on el valor de ddof indica els graus de llibertat, és a dir , utilitza $N-ddof$ al denominador en lloc de $N$ per poder-les utilitzar com a estimadors no esbiaixats en inferència estadística. Aquestes mesures es coneixen com a variància i desviació típica corregides.

Calculeu aquestes mesures per a la variable ROE.

In [341]:
# Standard deviation
df["ROE"].std(ddof=1)
Out[341]:
10.658735645941203
In [342]:
# Variance
df["ROE"].var(ddof=1)
Out[342]:
113.60864557005763

1.6 Mesures d'asimetria (skewness) i apuntalament (curtosi)¶

Aquestes mesures ens donen una idea de la forma de distribució de freqüències d'una variable amb un nombre. Òbviament la gràfica de la distribució és més clara, però en alguns casos, el nombre pot ser molt útil per prendre una decisió.

Simetria (skewness): Una distribució de freqüències és simètrica si la mitjana és igual a la mediana, en qualsevol altre cas, la distribució és asimètrica. Si la mitjana és menor a la mitjana, la distribució és asimètrica a l'esquerra (de cua esquerra o negativa) i indica que els valors estan més reunits a nivells superiors a la mitjana. En el cas oposat, la distribució és asimètrica a la dreta (de cua dreta o positiva), és a dir, tendeix a reunir-se més a la dreta de la mitjana, en els valors alts.

Apuntalament (curtosi): El grau de “punta” o “xafament” de la distribució d'una variable amb relació a la distribució normal es quantifica per mitjà d'aquest estadístic. En una distribució normal estàndard la curtosi és igual a 3 (Mesocúrtica). Una curtosi més gran indica una distribució que perfila un gràfic “més en punta” (Leptocúrtica). Una curtosi menor que 3 o negativa indica una distribució relativament més aplatada (Platicúrtica).

Calculeu aquestes mesures per a la variable AGE.

In [343]:
# Measure skweness
df["AGE"].skew()
Out[343]:
-0.22169097951636574
In [344]:
# Measure curtosis
df["AGE"].kurtosis()
Out[344]:
-0.17036404643751668
In [345]:
df["AGE"].hist(bins=50)
Out[345]:
<AxesSubplot:>
No description has been provided for this image

1.7 Resum de totes les estadístiques¶

El mètode descriu() ens proporciona una visió general ràpida de les dades numèriques en un DataFrame.

Calculeu aquestes mesures per a la variable AGE.

In [346]:
df["AGE"].describe()
Out[346]:
count    1700.000000
mean       61.862353
std        11.233668
min        26.000000
25%        54.000000
50%        63.000000
75%        70.000000
max        92.000000
Name: AGE, dtype: float64

En lloc de les mesures predefinides, es poden definir combinacions específiques d'estadístiques agregades per a columnes donades usant el mètode DataFrame.agg().

Calculeu aquestes mesures el mínim, màxim, la mitjana, la mediana, la desviació típica, el coeficient de skewness i de curtosi per a la variable ALT_BLOOD.

In [347]:
df.agg({'ALT_BLOOD': ['min', 'max', 'mean', 'std', 'var', 'skew', 'kurtosis']})
Out[347]:
ALT_BLOOD
min 0.030000
max 3.000000
mean 0.464506
std 0.355437
var 0.126336
skew 2.583580
kurtosis 9.172009

Finalment, es poden veure les mitges de les estadístiques per a totes les variables del conjunt de dades.

In [348]:
df.describe().loc[['mean']]
Out[348]:
ID L_BLOOD AGE ALT_BLOOD K_BLOOD ROE S_AD_ORIT AST_BLOOD Na_BLOOD TIME_B_S ... GIPER_Na ritm_ecg_p_04 fibr_ter_03 GT_POST fibr_ter_06 np09 zab_leg_04 n_r_ecg_p_02 n_p_ecg_p_05 ZSN_A
mean 850.5 8.725347 61.862353 0.464506 4.171471 13.033529 133.867647 0.256388 136.429412 4.633529 ... 0.017647 0.013529 0.04 0.004706 0.005294 0.001176 0.005294 0.004706 0.001176 0.188235

1 rows × 104 columns

1.8 Anàlisi gràfica univariable¶

La representació gràfica s'utilitza per facilitar la comprensió dels resultats, ja ho diu la dita popular “Val més una imatge que mil paraules”. Matplotlib és una llibreria de Python que permet la realització de gràfics de dades construïdes sobre matrius NumPy amb un disseny personalitzable, però és molt laboriós. D'altra banda, la llibreria Pandas també ofereix gràfiques integrades però amb funcionalitats limitades. Per contra, hi ha llibreries com Seaborn, que faciliten tant el treball amb les dades com la personalització dels gràfics. És una llibreria que es basa en Matplotlib però ofereix més avantatges ja que conté temes (plantilles de colors) i funcions estadístiques integrades.

1.8.1 Gràfic de barres¶

Aquest gràfic representa visualment la freqüència de variables categòriques mitjançant barres rectangulars de la mateixa amplada. A cada categoria o classe de la variable se li associa una barra l'altura de la qual representa la freqüència absoluta o la freqüència relativa d'aquesta classe.

Aquí feu la gràfica de barres per a l'outcome ZSN_A, que prèviament ha trobat les seves freqüències.

In [349]:
plt.bar(df["ZSN_A"].unique(), df["ZSN_A"].value_counts())
Out[349]:
<BarContainer object of 5 artists>
No description has been provided for this image

1.8.2 Diagrama de sectors¶

Aquest gràfic es representa com un cercle dividit en porcions, i aquestes són proporcionals a la freqüència relativa de cada categoria.

Feu aquest diagrama per a l'outcome ZSN_A.

In [350]:
plt.pie(df["ZSN_A"].value_counts(), labels=df["ZSN_A"].unique())
Out[350]:
([<matplotlib.patches.Wedge at 0x7f7c18e42160>,
  <matplotlib.patches.Wedge at 0x7f7c0b32ad00>,
  <matplotlib.patches.Wedge at 0x7f7c18e428b0>,
  <matplotlib.patches.Wedge at 0x7f7c18e42d90>,
  <matplotlib.patches.Wedge at 0x7f7c18e502b0>],
 [Text(-1.0410226929209416, 0.3553473692369641, '0.0'),
  Text(0.9819462580730486, -0.4957635991642973, '1.0'),
  Text(1.0726142477705103, -0.24392350333598994, '2.0'),
  Text(1.0920737375615726, -0.13181408015950996, '3.0'),
  Text(1.099322007085675, -0.03861508432107043, '4.0')])
No description has been provided for this image

1.8.3 Histograma¶

És la gràfica adequada per representar variables quantitatives amb un gran nombre de valors diferents. Les dades s'agrupen en intervals i es representen gràficament per rectangles juxtaposats les bases dels quals descansen sobre l'eix horitzontal i les altures del qual són tals que l'àrea de cada rectangle sigui proporcional a la freqüència de cada interval. Si tots els intervals tenen la mateixa longitud, llavors l'alçada de cada rectangle és proporcional a la freqüència l'interval. Per evitar confusions, la diferència principal amb el gràfic de barres és la inexistència d'espais entre rectangles.

Realitzeu aquesta representació gràfica per a la variable AGE.

In [351]:
plt.hist(df['AGE'])
Out[351]:
(array([  7.,  48., 104., 189., 261., 472., 314., 197.,  93.,  15.]),
 array([26. , 32.6, 39.2, 45.8, 52.4, 59. , 65.6, 72.2, 78.8, 85.4, 92. ]),
 <BarContainer object of 10 artists>)
No description has been provided for this image

1.8.4 Estimació de densitat de Kernel¶

Un histograma té com a objectiu aproximar la funció de densitat de probabilitat subjacent que va generar les dades en agrupar i comptar les observacions. L'estimació de la densitat del nucli (KDE) presenta una solució diferent del mateix problema. En lloc d'utilitzar contenidors discrets, un gràfic de KDE suavitza les observacions amb un nucli gaussià, produint una estimació de densitat contínua.

Continuant amb la variable AGE, realitzeu aquesta representació gràfica:

In [352]:
sns.histplot(df['AGE'], kde=True)
Out[352]:
<AxesSubplot:xlabel='AGE', ylabel='Count'>
No description has been provided for this image

1.8.5 Diagrama de caixes¶

Els diagrames de caixa, també coneguts com a gràfics de caixa i bigotis, ofereixen una representació gràfica compacta i eficient de distribucions de dades, encapsulant característiques fonamentals com la mediana (tendència central), la dispersió, la simetria i els valors atípics. Aquests gràfics utilitzen una caixa per mostrar el rang interquartílic (IQR), que abasta des del primer quartil (Q1) fins al tercer quartil (Q3), destacant així la meitat central de la distribució. La línia dins de la caixa marca la mitjana (Q2), proporcionant un indicador visual clar de la tendència central.

Els bigotis del diagrama s'estenen des de la caixa fins als valors mínim i màxim que cauen dins un límit determinat, típicament 1.5 vegades l'IQR a partir de les vores de la caixa. Aquesta convenció pot variar lleugerament a la literatura, però el propòsit és identificar els rangs típics de la variabilitat de les dades. Les dades que es troben més enllà dels extrems dels bigotis es consideren valors atípics i es representen comunament amb punts individuals, indicant observacions que difereixen significativament de la resta de la distribució.

L'orientació d'un diagrama de caixa pot ser horitzontal o vertical adaptant-se a les necessitats de presentació i comparació dins d'un conjunt de dades. La capacitat d'aquests diagrames per resumir simultàniament diversos aspectes estadístics fa que siguin eines valuoses per a l'exploració inicial de dades, permetent identificar ràpidament possibles anomalies, tendències i variacions a les dades.

Realitzeu aquesta representació gràfica per a la variable AGE:

In [353]:
sns.boxplot(x=df["AGE"])
Out[353]:
<AxesSubplot:xlabel='AGE'>
No description has been provided for this image

Seleccioneu algunes variables per crear la gràfica box plot. Com per exemple: L_BLOOD, AGE, ALT_BLOOD,K_BLOOD,ROE,S_AD_ORIT,AST_BLOOD,Na_BLOOD,TIME_B_S.

In [354]:
columns = ["L_BLOOD", "AGE", "ALT_BLOOD","K_BLOOD","ROE","S_AD_ORIT","AST_BLOOD","Na_BLOOD","TIME_B_S"]
for column in columns:
    sns.boxplot(x=df[column])
    plt.title(f'Boxplot of {column}')  # Sets the title to the name of the current column
    plt.show()
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image

1.8.6 Diagrama de violí¶

El diagrama de violí és una eina de visualització que combina les característiques informatives del diagrama de caixa amb l'estimació de densitat del nucli per proporcionar una vista més rica i detallada de la distribució de les dades. Aquest tipus de gràfic no només destaca la mitjana, el rang interquartílic i els valors atípics, com ho fa un diagrama de caixa tradicional, sinó que també ofereix una representació suavitzada de la densitat de les dades en diferents valors, reflectint la probabilitat de trobar punts de dades a qualsevol regió específica de la distribució.

La forma característica de "violí" del gràfic sorgeix de la duplicació simètrica de l'estimació de densitat de nucli al llarg de l'eix central, cosa que facilita la comparació visual de la forma de la distribució, els seus pics i valls, entre diferents grups o categories. A més de la mitjana i el rang interquartílic, que solen estar marcats dins del "cos" del violí, alguns diagrames de violí també inclouen marcadors per a la mitjana i un interval de confiança del 95%, proporcionant així indicadors addicionals sobre la ubicació central de la distribució i la variabilitat de les dades.

En integrar aquests elements, el diagrama de violí es converteix en una eina excepcionalment poderosa per a l'exploració de dades, permetent als analistes i científics de dades visualitzar no només les tendències centrals i la dispersió, sinó també l'estructura completa de la distribució dels dades. Això és particularment útil per identificar distribucions bimodals o multimodals i per comparar la distribució de les dades a través de diferents categories o grups de manera intuïtiva i efectiva.

Realitzeu aquesta representació gràfica per a la variable AGE:

In [355]:
sns.violinplot(df['AGE'])
Out[355]:
<AxesSubplot:ylabel='AGE'>
No description has been provided for this image

Utilitzant les variables seleccionades anteriorment, realitzeu el diagrama de violí.

In [356]:
for column in columns:
    sns.violinplot(x=df[column])
    plt.title(f'Violinplot of {column}')  # Sets the title to the name of the current column
    plt.show()
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image

Per poder provar diversos models, primer dividirem el dataset entre train i test. A més, perquè tots obtingueu els mateixos resultats i poder comentar dubtes pel fòrum/correu, fixarem la seed per obtenir els mateixos datasets de train i test.

Com que en aquest exercici tractarem stacking i cascading, i tots dos s'apliquen sobre el conjunt de tests, farem un split del 70% per tenir una mica més de base en aplicar aquestes dues tècniques.

In [357]:
# test train split]
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.7)

1.9 Anàlisi bivariable¶

L'anàlisi bivariable s'utilitza per trobar la relació existent entre dues variables, ja siguin ambdues categòriques, ambdues numèriques o combinació entre elles. Hi ha tres maneres comunes per fer una anàlisi bivariable:

Gràficament: El diagrama de dispersió és adequat per analitzar dues variables numèriques i indica la relació lineal o no lineal entre les variables. Per altra banda, els gràfics de barres ajuden a comprendre la relació entre dues variables categòriques. La resta de gràfiques vistes anteriorment (histogrames, estimació de densitat, diagrama de caixes, de violí, etc.) també ens poden donar una idea de la relació entre una variable numèrica i una categòrica.

Coeficients de correlació: Són mesures específiques que quantifica la intensitat de la relació lineal entre dues variables. Són bàsicament una comparació entre la distància de cada dada puntual respecte a la mitjana de la variable i utilitza aquesta comparació per dir-nos fins a quin punt la relació entre les variables s'ajusta a una línia imaginària traçada entre les dades.

Realitzeu un histplot, utilitzant la variable AGE, stat opció probability, igualment incloent l'outcome ZSN_A i kde=True:

In [358]:
sns.histplot(data=df, x='AGE', hue='ZSN_A', stat='probability', common_norm=False, kde=True)
Out[358]:
<AxesSubplot:xlabel='AGE', ylabel='Probability'>
No description has been provided for this image
In [359]:
# Maybe it is easier to visualize like this:
sns.histplot(data=df, x='AGE', hue='ZSN_A', stat='probability', element='step', bins=10, common_norm=False, kde=True)
Out[359]:
<AxesSubplot:xlabel='AGE', ylabel='Probability'>
No description has been provided for this image

Utilitzant la selecció de variables, realitzeu el pairplot tenint en compte l'outcome ZSN_A. Podeu crear altres conjunts de variables.

In [360]:
smaller_df= pd.DataFrame()
In [361]:
smaller_df = df[columns].copy()
smaller_df["ZSN_A"]= df['ZSN_A'].copy()
In [362]:
sns.pairplot(smaller_df, hue="ZSN_A")
Out[362]:
<seaborn.axisgrid.PairGrid at 0x7f7c1b935610>
No description has been provided for this image

Realitzeu l'scatter plot entre les variables K_BLOOD i L_BLOOD tenint en compte l'outcome ZSN_A.

In [368]:
sns.scatterplot(smaller_df, x='K_BLOOD', y='L_BLOOD', hue='ZSN_A')
Out[368]:
<AxesSubplot:xlabel='K_BLOOD', ylabel='L_BLOOD'>
No description has been provided for this image
In [369]:
# I personally would do it like this:
outcomes = smaller_df['ZSN_A'].unique()

fig, axes = plt.subplots(ncols=len(outcomes), figsize=(20, 4), sharex=True, sharey=True)
                         
for i, outcome in enumerate(outcomes):
    grouped_df = smaller_df[smaller_df['ZSN_A']==outcome]
    sns.scatterplot(grouped_df, x='K_BLOOD', y='L_BLOOD', ax=axes[i])
    axes[i].set_title(f"Outcome of ZSN_A is: {outcome}")
No description has been provided for this image
In [370]:
# Or same but with a reglplot
outcomes = smaller_df['ZSN_A'].unique()

fig, axes = plt.subplots(ncols=len(outcomes), figsize=(25, 4), sharex=True, sharey=True)
                         
for i, outcome in enumerate(outcomes):
    grouped_df = smaller_df[smaller_df['ZSN_A']==outcome]
    sns.regplot(grouped_df, x='K_BLOOD', y='L_BLOOD', ax=axes[i])
    axes[i].set_title(f"Outcome of ZSN_A is: {outcome}")
No description has been provided for this image

Realitzeu una distribució bivariada utilitzant Seaborn entre les variables ROE i AST_BLOOD tenint en compte l'outcome ZSN_A:

In [371]:
sns.scatterplot(smaller_df, x='ROE', y='AST_BLOOD', hue='ZSN_A')
Out[371]:
<AxesSubplot:xlabel='ROE', ylabel='AST_BLOOD'>
No description has been provided for this image
In [372]:
# Or same but with a reglplot
outcomes = smaller_df['ZSN_A'].unique()

fig, axes = plt.subplots(ncols=len(outcomes), figsize=(20, 4), sharex=True, sharey=True)
                         
for i, outcome in enumerate(outcomes):
    grouped_df = smaller_df[smaller_df['ZSN_A']==outcome]
    sns.regplot(grouped_df, x='ROE', y='AST_BLOOD', ax=axes[i])
    axes[i].set_title(f"Outcome of ZSN_A is: {outcome}")
    axes[i].set_ylim([0, 2.2])
No description has been provided for this image

Coeficients de correlació¶

En fer un diagrama de dispersió entre dues variables podem identificar tres tipus de correlació entre elles:

Correlació negativa: Els valors de y tendeixen a disminuir a mesura que augmenten els valors de x. Això mostra una forta correlació negativa, que passa quan valors grans duna característica corresponen a valors petits de laltra, i viceversa.

Fèbil o sense correlació: No mostra una tendència òbvia. Aquesta és una forma de correlació feble, que passa quan una associació entre dues característiques no és òbvia o és difícilment observable.

Correlació positiva: Els valors de y tendeixen a augmentar a mesura que augmenten els valors de x. Això il·lustra una forta correlació positiva, que passa quan valors grans duna característica corresponen a valors grans de laltra, i viceversa.

Quan s'analitza la correlació, sempre cal tenir en compte que la correlació no indica causalitat. Quantifica la força de relació entre les característiques d'un conjunt de dades. De vegades, l'associació és causada per un factor comú a diverses característiques d'interès.

A Pandas el mètode .corr() es pot utilitzar per calcular tres coeficients de correlació (Pearson, Spearman i Kendall) o qualsevol mètode editable.

In [82]:
df.corr(method='pearson')
Out[82]:
ID L_BLOOD AGE ALT_BLOOD K_BLOOD ROE S_AD_ORIT AST_BLOOD Na_BLOOD TIME_B_S ... GIPER_Na ritm_ecg_p_04 fibr_ter_03 GT_POST fibr_ter_06 np09 zab_leg_04 n_r_ecg_p_02 n_p_ecg_p_05 ZSN_A
ID 1.000000 0.165842 0.213113 -0.063048 -0.054767 0.066894 -0.155869 0.002653 -0.027627 -0.116248 ... -0.014430 0.096467 0.086920 0.036185 -0.025693 -0.053010 0.036512 0.029687 0.034128 0.047305
L_BLOOD 0.165842 1.000000 0.001554 0.044013 0.014832 0.008114 -0.114370 0.075467 0.014114 -0.102946 ... 0.017224 0.002667 0.046766 0.015199 0.031578 -0.042138 0.018969 0.036172 0.017531 0.048912
AGE 0.213113 0.001554 1.000000 -0.100174 -0.006518 0.197549 0.045591 -0.052900 0.025094 -0.047046 ... -0.028592 0.030906 -0.169636 0.048295 -0.069120 -0.019443 0.016052 0.029926 0.040149 0.120281
ALT_BLOOD -0.063048 0.044013 -0.100174 1.000000 0.019434 -0.009963 -0.079810 0.522017 -0.009438 0.019828 ... 0.055006 -0.017821 -0.022610 0.006627 0.012534 0.020331 -0.005944 -0.011515 -0.008162 0.009486
K_BLOOD -0.054767 0.014832 -0.006518 0.019434 1.000000 0.012192 0.018944 0.046188 0.300705 0.080468 ... 0.158989 0.026362 -0.032646 0.023540 -0.021168 0.029743 0.063832 -0.004787 -0.003674 0.020274
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
np09 -0.053010 -0.042138 -0.019443 0.020331 0.029743 -0.016212 -0.004606 0.008101 -0.002562 -0.014057 ... -0.004600 -0.004019 -0.007006 -0.002360 -0.002504 1.000000 -0.002504 -0.002360 -0.001178 -0.009956
zab_leg_04 0.036512 0.018969 0.016052 -0.005944 0.063832 0.030960 -0.012603 -0.010419 -0.016722 -0.007913 ... -0.009778 -0.008544 -0.014892 -0.005016 -0.005322 -0.002504 1.000000 -0.005016 -0.002504 -0.021164
n_r_ecg_p_02 0.029687 0.036172 0.029926 -0.011515 -0.004787 0.019949 -0.016684 -0.009355 -0.026055 0.024634 ... -0.009216 -0.008053 -0.014036 -0.004728 -0.005016 -0.002360 -0.005016 1.000000 -0.002360 -0.019947
n_p_ecg_p_05 0.034128 0.017531 0.040149 -0.008162 -0.003674 0.007944 -0.046285 -0.006759 -0.002562 0.004545 ... -0.004600 -0.004019 -0.007006 -0.002360 -0.002504 -0.001178 -0.002504 -0.002360 1.000000 -0.009956
ZSN_A 0.047305 0.048912 0.120281 0.009486 0.020274 0.021462 -0.042863 -0.001787 -0.018979 -0.007118 ... -0.025111 -0.033973 -0.059215 -0.019947 -0.021164 -0.009956 -0.021164 -0.019947 -0.009956 1.000000

104 rows × 104 columns

In [83]:
df.corr(method='spearman')
Out[83]:
ID L_BLOOD AGE ALT_BLOOD K_BLOOD ROE S_AD_ORIT AST_BLOOD Na_BLOOD TIME_B_S ... GIPER_Na ritm_ecg_p_04 fibr_ter_03 GT_POST fibr_ter_06 np09 zab_leg_04 n_r_ecg_p_02 n_p_ecg_p_05 ZSN_A
ID 1.000000 0.174530 0.213500 -0.070312 -0.055582 0.063879 -0.115106 0.001847 -0.046077 -0.112542 ... -0.014430 0.096467 0.086920 0.036185 -0.025693 -0.053010 0.036512 0.029687 0.034128 -0.079351
L_BLOOD 0.174530 1.000000 0.012622 0.010579 0.008495 -0.008840 -0.097681 0.040632 -0.007923 -0.103573 ... 0.007732 0.019099 0.056602 0.016628 0.031298 -0.052158 -0.003363 0.036244 0.023351 0.038419
AGE 0.213500 0.012622 1.000000 -0.115943 -0.018558 0.212178 0.077114 -0.074974 0.007550 -0.034547 ... -0.030767 0.035252 -0.165470 0.052734 -0.068530 -0.007906 0.015881 0.029692 0.044149 0.121550
ALT_BLOOD -0.070312 0.010579 -0.115943 1.000000 0.021411 -0.009946 -0.067855 0.509998 0.012675 0.025233 ... 0.032465 -0.003894 -0.021003 0.014086 0.020561 0.035100 0.001919 -0.000301 0.000141 -0.004886
K_BLOOD -0.055582 0.008495 -0.018558 0.021411 1.000000 -0.002136 0.010957 0.038051 0.287098 0.097939 ... 0.123672 0.001345 -0.045503 0.016990 -0.018764 0.027076 0.043353 0.006273 0.000106 0.011426
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
np09 -0.053010 -0.052158 -0.007906 0.035100 0.027076 -0.014040 -0.005297 0.008694 -0.004827 -0.007593 ... -0.004600 -0.004019 -0.007006 -0.002360 -0.002504 1.000000 -0.002504 -0.002360 -0.001178 -0.011718
zab_leg_04 0.036512 -0.003363 0.015881 0.001919 0.043353 0.023735 -0.009362 0.006959 -0.003150 -0.002844 ... -0.009778 -0.008544 -0.014892 -0.005016 -0.005322 -0.002504 1.000000 -0.005016 -0.002504 -0.024908
n_r_ecg_p_02 0.029687 0.036244 0.029692 -0.000301 0.006273 0.022759 -0.017905 -0.004896 -0.035830 0.023324 ... -0.009216 -0.008053 -0.014036 -0.004728 -0.005016 -0.002360 -0.005016 1.000000 -0.002360 -0.023477
n_p_ecg_p_05 0.034128 0.023351 0.044149 0.000141 0.000106 0.019919 -0.031925 0.003612 -0.004827 -0.001254 ... -0.004600 -0.004019 -0.007006 -0.002360 -0.002504 -0.001178 -0.002504 -0.002360 1.000000 -0.011718
ZSN_A -0.079351 0.038419 0.121550 -0.004886 0.011426 0.053387 -0.015079 -0.030352 -0.017657 -0.002250 ... -0.017910 -0.039985 -0.069693 -0.023477 -0.024908 -0.011718 -0.024908 -0.023477 -0.011718 1.000000

104 rows × 104 columns

In [84]:
df.corr(method='kendall')
Out[84]:
ID L_BLOOD AGE ALT_BLOOD K_BLOOD ROE S_AD_ORIT AST_BLOOD Na_BLOOD TIME_B_S ... GIPER_Na ritm_ecg_p_04 fibr_ter_03 GT_POST fibr_ter_06 np09 zab_leg_04 n_r_ecg_p_02 n_p_ecg_p_05 ZSN_A
ID 1.000000 0.116703 0.144797 -0.048414 -0.038322 0.043893 -0.080464 0.001192 -0.030603 -0.080973 ... -0.011785 0.078788 0.070991 0.029554 -0.020985 -0.043295 0.029821 0.024246 0.027873 -0.065649
L_BLOOD 0.116703 1.000000 0.008352 0.007379 0.005844 -0.006119 -0.069245 0.028862 -0.005574 -0.073273 ... 0.006366 0.015724 0.046598 0.013690 0.025766 -0.042940 -0.002768 0.029839 0.019224 0.031109
AGE 0.144797 0.008352 1.000000 -0.082974 -0.012769 0.148083 0.054910 -0.053246 0.005201 -0.025058 ... -0.025457 0.029168 -0.136913 0.043633 -0.056704 -0.006542 0.013140 0.024568 0.036530 0.098885
ALT_BLOOD -0.048414 0.007379 -0.082974 1.000000 0.015101 -0.007227 -0.050956 0.409324 0.010793 0.018627 ... 0.027920 -0.003349 -0.018063 0.012114 0.017683 0.030186 0.001651 -0.000259 0.000122 -0.004121
K_BLOOD -0.038322 0.005844 -0.012769 0.015101 1.000000 -0.000513 0.007976 0.028866 0.218048 0.071913 ... 0.104905 0.001141 -0.038598 0.014412 -0.015917 0.022967 0.036774 0.005321 0.000090 0.009588
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
np09 -0.043295 -0.042940 -0.006542 0.030186 0.022967 -0.011806 -0.004599 0.007499 -0.004181 -0.006585 ... -0.004600 -0.004019 -0.007006 -0.002360 -0.002504 1.000000 -0.002504 -0.002360 -0.001178 -0.011535
zab_leg_04 0.029821 -0.002768 0.013140 0.001651 0.036774 0.019958 -0.008129 0.006002 -0.002728 -0.002467 ... -0.009778 -0.008544 -0.014892 -0.005016 -0.005322 -0.002504 1.000000 -0.005016 -0.002504 -0.024520
n_r_ecg_p_02 0.024246 0.029839 0.024568 -0.000259 0.005321 0.019137 -0.015546 -0.004223 -0.031033 0.020226 ... -0.009216 -0.008053 -0.014036 -0.004728 -0.005016 -0.002360 -0.005016 1.000000 -0.002360 -0.023110
n_p_ecg_p_05 0.027873 0.019224 0.036530 0.000122 0.000090 0.016749 -0.027719 0.003116 -0.004181 -0.001087 ... -0.004600 -0.004019 -0.007006 -0.002360 -0.002504 -0.001178 -0.002504 -0.002360 1.000000 -0.011535
ZSN_A -0.065649 0.031109 0.098885 -0.004121 0.009588 0.044056 -0.012937 -0.025724 -0.015136 -0.001917 ... -0.017631 -0.039360 -0.068605 -0.023110 -0.024520 -0.011535 -0.024520 -0.023110 -0.011535 1.000000

104 rows × 104 columns

També en lloc d'utilitzar els mètodes estàndard de correlació (com Pearson, Spearman o Kendall), defina i utilitzeu la funció personalitzada anomenada histogram_intersection per calcular la correlació. Aquesta operació no és una mesura de correlació en el sentit tradicional (com Pearson, que mesura la relació lineal entre variables), sinó més aviat una mesura de la quantitat d'intersecció o solapament entre els valors de dues variables.

Source: https://mpatacchiola.github.io/blog/2016/11/12/the-simplest-classifier-histogram-intersection.html

In [88]:
def histogram_intersection(var1, var2):
    """
    Return the histogram intersection of two variables. Does not handle Nan.
    """
    hist_1, _ = np.histogram(var1, bins=100, range=[-15, 15])
    hist_2, _ = np.histogram(var2, bins=100, range=[-15, 15])
    minima = np.minimum(hist_1, hist_2)
    intersection = np.true_divide(np.sum(minima), np.sum(hist_2))
    return intersection
In [91]:
histogram_intersection(df['L_BLOOD'], df['ALT_BLOOD'])
Out[91]:
0.002352941176470588

Finalment, visualitzeu la matriu de correlacions utilitzant un mapa de calor "heatmap".

In [92]:
sns.heatmap(df.corr(method='pearson'))
Out[92]:
<AxesSubplot:>
No description has been provided for this image

Trobeu alternatives per visualitzar millor la matriu de correlació en cas que hi hagi massa variables, com per exemple, el grup de variables seleccionades.

In [93]:
sns.heatmap(smaller_df.corr(method='pearson'))
Out[93]:
<AxesSubplot:>
No description has been provided for this image

Ara com a analista de dades tens una idea general de la base de dades. Aplica els conceptes aplicats durant el curs.

Dividiu les dades: Com que en aquest exercici tractarem stacking i cascading, i tots dos s'apliquen sobre el conjunt de tests, farem un split del 70% per tenir una mica més de base en aplicar aquestes dues tècniques. Aquest pas garanteix que qualsevol transformació que apliqui per equilibrar o escalar les dades es faci de manera independent en el conjunt d'entrenament per evitar la fugida de dades.

In [373]:
# test train split]
from sklearn.model_selection import train_test_split

# It says test should be 0.7, but the teacher wrote on the forum that is an error, it should be train 0.7

X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.7)

Determina si cal equilibrar les dades: un cop s'han dividit les dades, analitza si cal equilibrar el conjunt d'entrenament. Si cal, utilitza alguna de les tècniques conegudes com ara sobremostrejar la classe minoritària, submostrejar la classe majoritària o mètodes de generació de dades sintètiques com SMOTE. Recordeu que és fonamental equilibrar únicament el conjunt d'entrenament per mantenir la integritat i la distribució realista del conjunt de prova. Com que el conjunt de les dades per a entrenament és 30 % i dins algunes classes estan subrepresentades, incloeu al paràmetre k_neighbors=2 a l'hora d'inicialitzar l'SMOTE.

In [374]:
X.columns
Out[374]:
Index(['ID', 'L_BLOOD', 'AGE', 'ALT_BLOOD', 'K_BLOOD', 'ROE', 'S_AD_ORIT',
       'AST_BLOOD', 'Na_BLOOD', 'TIME_B_S',
       ...
       'fibr_ter_01', 'GIPER_Na', 'ritm_ecg_p_04', 'fibr_ter_03', 'GT_POST',
       'fibr_ter_06', 'np09', 'zab_leg_04', 'n_r_ecg_p_02', 'n_p_ecg_p_05'],
      dtype='object', length=103)
In [375]:
y_train.unique()
Out[375]:
array([0., 2., 1., 3., 4.])
In [376]:
len(y_train), y_train.value_counts(normalize=True)
Out[376]:
(1190,
 0.0    0.903361
 1.0    0.056303
 3.0    0.016807
 2.0    0.012605
 4.0    0.010924
 Name: ZSN_A, dtype: float64)
In [377]:
# Smote
smote = SMOTE(random_state=myseed, k_neighbors=2)
X_train_smote, y_train_smote = smote.fit_resample(X_train, y_train)
In [378]:
len(y_train_smote), y_train_smote.value_counts(normalize=True)
Out[378]:
(5375,
 0.0    0.2
 2.0    0.2
 1.0    0.2
 3.0    0.2
 4.0    0.2
 Name: ZSN_A, dtype: float64)

Escala les dades: Aplica l'estandardització o la normalització per escalar les variables. Els paràmetres d'escala (per exemple, mitjana i desviació estàndard utilitzats per a l'estandardització) determinats a partir del conjunt d'entrenament s'han d'aplicar al conjunt de prova.

In [380]:
scaler = StandardScaler()
X_train_smote_scaled = scaler.fit_transform(X_train_smote)
X_test_scaled = scaler.transform(X_test)

2. Combinació de classificadors (5 punts)¶

A la primera part d'aquest exercici s'aplicarà la combinació de classificadors en paral·lel mitjançant les tècniques de Bagging i Boosting.

A la segona part s'utilitzaran tècniques de combinació seqüencial de classificadors: Stacking i Cascading.

2.1. Combinació paral·lela de classificadors (2.5 punts)¶

L'aprenentatge conjunt és un enfocament poderós a l'aprenentatge automàtic que combina estratègicament múltiples models per abordar tasques de predicció complexes, millorant la precisió general i la solidesa de les prediccions. Aquest mètode aprofita les fortaleses de diversos alumnes per compensar les febleses dels models individuals.

Compensació de biaix i variància:

  • Sesgo: Representa errors introduïts en aproximar-se a un problema del món real, que pot ser massa simplista. Un alt biaix pot fer que es perdin relacions essencials entre les característiques i els resultats objectiu, cosa que resulta en un desajust.
  • Variança: Indica la sensibilitat de l'algorisme a conjunts específics de dades d'entrenament. Els models d'alta variància tendeixen a modelar el soroll aleatori en les dades d'entrenament en lloc dels resultats previstos, fet que porta a un sobreajustament.

Per comprendre amb més detall la compensació de biaix i variància en els models d'aprenentatge automàtic, podeu consultar aquest article.

Explicació de les tècniques de conjunt:

  1. Bagging (agregació de Bootstrap): redueix la variància en crear múltiples models a partir de diferents subconjunts de dades d'entrenament i fer una mitjana de les seves prediccions. Per exemple, l'algorisme Random Forest és un tipus de conjunt d'embossat.
  2. Boosting: té com a objectiu reduir el biaix aplicant seqüencialment una sèrie de models febles i centrant-se a corregir els errors comesos per models anteriors. Tècniques com AdaBoost, Gradient Boosting i XGBoost són mètodes d'impuls populars.
  3. Stacking: integra prediccions de nombrosos models i utilitza un nou model per realitzar una predicció final, cosa que millora substancialment el rendiment predictiu.
  4. Cascading: s'empra en aplicacions crítiques on els errors tenen conseqüències importants, com la detecció de frau, utilitzant un enfocament en capes per refinar les decisions de forma progressiva.

Els mètodes de conjunt són particularment efectius perquè combinen les prediccions de múltiples models per millorar-ne la confiança i la precisió. En reduir el biaix i la variància, aquestes tècniques minimitzen l'error de generalització total, cosa que és essencial per a solucions sòlides d'aprenentatge automàtic.

2.1.1. Arbres de decisió¶

Per poder comparar l'augment de performance obtingut a mesura que anem aplicant tècniques noves, farem servir com a baseline un simple arbre de decisió.

Implementació: Defineix un arbre de decisió amb profunditat màxima de 3 nivells (aplicarem la mateixa restricció a les següents seccions), utilitzant la llavor definida a l'apartat anterior i calcula la seva precisió sobre el conjunt de train aplicant validació creuada amb 5 conjunts. A continuació, entrena l'arbre de decisió sobre el conjunt de dades de train i avalua sobre test utilitzant la precisió com métrica ('accuracy'). Suggeriment: utilitzar el mòdul cross_val_score de sklearn.

Recordeu que existeix el material sobre cross validation i sobre com utilitzar aquests mòduls. Aquesta informació està disponible als següents enllaços:

  • cross_val_score
  • cross_validation
In [381]:
dtree = DecisionTreeClassifier(max_depth=3, random_state=myseed)
In [382]:
scores = cross_val_score(dtree, X_train_smote_scaled, y_train_smote, cv=5)
scores
Out[382]:
array([0.59906977, 0.58976744, 0.61488372, 0.62046512, 0.60651163])
In [383]:
#If we want to test on the test split
dtree.fit(X_train_smote_scaled, y_train_smote)
Out[383]:
DecisionTreeClassifier(max_depth=3, random_state=13)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
DecisionTreeClassifier(max_depth=3, random_state=13)
In [384]:
y_pred = dtree.predict(X_test_scaled)
In [385]:
accuracy_score(y_test, y_pred)
Out[385]:
0.5666666666666667

With the baseline model decision tree, we have achieved an accuracy score of 0.56 on the test data.

Let's write s small function to reuse code in the future:

In [386]:
def train_and_predict(model):
    """
    Train and predict the current dataset based on a given model
    """
    model.fit(X_train_smote_scaled, y_train_smote)
    y_pred = model.predict(X_test_scaled)
    acc_score = accuracy_score(y_pred, y_test)
    
    #regex to get the name of the model from the class
    model_name = str(type(model)).split(".")[-1][:-2]
    
    print(f"With model {model_name}, we have achieved an accuracy score of {acc_score:.3f} on the test data.")
    return {model_name:acc_score}
In [387]:
# We verify it works
dtree_acc = train_and_predict(dtree)
With model DecisionTreeClassifier, we have achieved an accuracy score of 0.567 on the test data.

2.1.2. Bagging¶

La idea central del bagging és fer servir rèpliques del conjunt de dades original i fer-les servir per entrenar diferents classificadors.

Crearem subconjunts mostrant aleatòriament un munt de punts del conjunt de dades d'entrenament amb reemplaçament.

Ara entrenarem classificadors individuals a cadascun d'aquests subconjunts bootstrap.

Cadascú d'aquests classificadors base predirà l'etiqueta de classe per a un problema donat. Aquí és on combinem les prediccions de tots els models base. Aquesta part s'anomena etapa d'agregació. És per això que trobareu els ensembles bagging pel nom d'ensembles d'agregació.

En general, es fa servir un vot de majoria simple en un sistema de classificació i es pren la mitjana de totes les prediccions per als models de regressió per combinar tots els classificadors base en un sol model i proporcionar el resultat final del model de conjunt.

Un exemple simple d'aquest enfocament és l'algorisme Random Forest. El bagging redueix l'alta variació (variança) d'un model, reduint així l'error de generalització. És un mètode molt eficaç, especialment quan tenim dades molt limitades amb el nostre cas.

Mitjançant lús de mostres de bootstrap, podem obtenir una estimació afegint les puntuacions de moltes mostres.

Com faríem bagging?

Suposem que tenim un conjunt d´entrenament que conté 100.000 punts de dades.

Crearíem N subconjunts mostrant a l'atzar 50K punts de dades per a cada subconjunt.

Cadascun daquests N subconjunts sutilitzarà per entrenar N classificadors diferents.

A l'etapa d'agregació, totes aquestes N prediccions es combinaran en un sol model, també anomenat metaclassificador.

Dels 100.000 punts presents originalment al conjunt de dades, si eliminem 1000 punts, l'impacte que tindrà als conjunts de dades mostrades serà molt inferior.

Si pensem intuïtivament, és possible que alguns d'aquests 1000 punts no siguin presents a tots els conjunts de dades mostrejades i, per tant, la quantitat de punts que s'eliminaran de cada conjunt de dades mostrejades serà molt inferior. Fins i tot zero en alguns casos! En resum, l'impacte d'eliminar 1000 punts d'aquest tipus serà en realitat menor als classificadors base, cosa que reduirà la variació en un model i ho farà més sòlid.

La variància no és res més que sensibilitat al soroll, com hem comentat anteriorment.

Implementació: Defineix un Random Forest Classifier amb 20 arbres de decisió i profunditat màxima de 3 nivells, utilitzant la llavor definida a l'inici del segon exercici, i calcula la seva precisió sobre el conjunt de train aplicant validació creuada amb 5 conjunts. A continuació, entrena el model sobre el conjunt de dades de train i avalua sobre test utilitzant la precisió com a métrica ('accuracy').

Suggeriment: Usar el mòdul RandomForestClassifier de sklearn.

Per a aprendre a fer servir aquest mòdul us recomanem el següent:

  • RandomForestClassifier
In [388]:
from sklearn.ensemble import RandomForestClassifier

rand_forest = RandomForestClassifier()
In [389]:
cross_val_score(rand_forest, X_train_smote_scaled, y_train_smote, cv=5)
Out[389]:
array([0.9172093 , 1.        , 0.99906977, 1.        , 1.        ])
In [390]:
rand_forest_acc = train_and_predict(rand_forest)
With model RandomForestClassifier, we have achieved an accuracy score of 0.876 on the test data.

2.1.3. Boosting¶

El boosting sutilitza per convertir els classificadors de base feble en forts. Els classificadors febles generalment tenen una correlació molt feble amb les etiquetes de classe veritables i els classificadors forts tenen una correlació molt alta entre el model i les etiquetes de classe veritables.

El boosting capacita els classificadors febles de manera iterativa, cadascun tractant de corregir l'error comès pel model anterior. Això s'aconsegueix entrenant un model feble en totes les dades d'entrenament, tot construint un segon model que té com a objectiu corregir els errors comesos pel primer model. Després construïm un tercer model que intenta corregir els errors comesos pel segon model i així successivament. Els models s'agreguen de manera iterativa fins que el model final ha corregit tots els errors comesos per tots els models anteriors.

Quan s'afegeixen els models a cada etapa, s'assignen alguns pesos al model relacionat amb la precisió del model anterior. Després dafegir un classificador feble, els pesos es tornen a ajustar. Els punts classificats incorrectament reben pesos més alts i els punts classificats correctament reben pesos més baixos. Aquest enfocament farà que el següent classificador se centri en els errors comesos pel model anterior.

El boosting redueix l'error de generalització prenent un model d'alt bias i baixa variància i reduint el bias en un nivell significatiu. Recordeu, el bagging redueix la variància. Com el bagging, el boosting també ens permet treballar amb models de classificació i regressió.

Implementació: Defineix un Gradient Boosting Classifier amb 20 arbres de decisió i profunditat màxima de 3 nivells, utilitzant la llavor definida a l'inici del segon exercici, i calcula la seva precisió sobre el conjunt de train aplicant validació creuada amb 5 conjunts. A continuació, entrena el model sobre el conjunt de dades de train i avalua sobre test utilitzant la precisió com a métrica ('accuracy').

Suggeriment:

  • Usar el mòdul GradientBoostingClassifier de sklearn.
In [391]:
from sklearn.ensemble import GradientBoostingClassifier
grad_boost = GradientBoostingClassifier(n_estimators=20, random_state=myseed, max_depth=3)

cross_val_score(grad_boost, X_train_smote_scaled, y_train_smote, cv=5)
Out[391]:
array([0.92465116, 0.9655814 , 0.96465116, 0.97860465, 0.97488372])
In [392]:
grad_boost_acc = train_and_predict(grad_boost)
With model GradientBoostingClassifier, we have achieved an accuracy score of 0.824 on the test data.

2.1.4. Anàlisi¶

Analitzeu els resultats obtinguts amb la combinació paral·lela de classificadors.

In [393]:
comb_dict = {**dtree_acc, **rand_forest_acc, **grad_boost_acc}
In [394]:
acc_df = pd.DataFrame(list(comb_dict.items()), columns=['Model', 'Accuracy'])
In [395]:
acc_df
Out[395]:
Model Accuracy
0 DecisionTreeClassifier 0.566667
1 RandomForestClassifier 0.876471
2 GradientBoostingClassifier 0.823529
In [396]:
sns.barplot(x='Model', y='Accuracy', data=acc_df)
plt.xticks(rotation=30)
Out[396]:
([0, 1, 2], [Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, '')])
No description has been provided for this image

Veiem que el millor model es el RandomForest classifier, seguit del GradientBoosting Classifier i per ultim el Decision Tree. També observem que en el training set obtenim scores molt mes altes que no pass en el test set, aixo es indicació d'overfitting i hauriem de fer alguna cosa per solucionar-ho, per exemple afegir regularitzacions. El cas més geu es el RandomForest que té varies scores del 100% en el training.

2.2. Combinació seqüencial de classificadors base diferents (2.5 punts)¶

Per poder fer combinació seqüencial de models, necessitem tenir diversos models diferents entrenats. En el nostre cas ja tenim un arbre de decisió. Entrenarem un parell de models més.

2.2.1. KNN (k veïns més propers)¶

Implementació: Defineix un K-Neighbors Classifier amb 2 veïns i calcula la seva precisió sobre el conjunt de train aplicant validació creuada amb 5 conjunts. A continuació, entrena el model sobre el conjunt de dades de train i avalua sobre test utilitzant la precisió com a mètrica ('accuracy').

In [397]:
knn_classifier = KNeighborsClassifier(n_neighbors=2)

cross_val_score(knn_classifier, X_train_smote_scaled, y_train_smote, cv=5)
Out[397]:
array([0.96372093, 0.97116279, 0.97116279, 0.97209302, 0.96744186])
In [398]:
knn_acc = train_and_predict(knn_classifier)
With model KNeighborsClassifier, we have achieved an accuracy score of 0.753 on the test data.

2.2.2. SVM (Support Vector Machines)¶

Implementació: Defineix un SVM amb gamma = 0.07 i calcula la seva precisió sobre el conjunt de train aplicant validació creuada amb 5 conjunts. A continuació, entrena el model sobre el conjunt de dades de train i avalua sobre test utilitzant la precisió com a mètrica ('accuracy').

In [399]:
from sklearn.svm import SVC
gamma=0.07
svm = SVC(gamma=gamma, random_state=myseed)
In [400]:
cross_val_score(svm, X_train_smote_scaled, y_train_smote, cv=5)
Out[400]:
array([0.99627907, 0.99813953, 0.99627907, 0.9972093 , 0.9972093 ])
In [401]:
svm_acc = train_and_predict(svm)
With model SVC, we have achieved an accuracy score of 0.876 on the test data.

2.2.3. Stacking¶

Tots els models individuals s'entrenen per separat en el conjunt complet de dades d'entrenament i s'ajusten per aconseguir més precisió. La compensació de bias i variància es té en compte per a cada model. El model final, també conegut com a metaclassificador, s'alimenta de les etiquetes de classe predites pels models base o de les probabilitats predites per a cada etiqueta de classe. Després, el metaclassificador s'entrena en funció dels resultats donats pels models base.

A l'stacking, s'entrena un nou model en funció de les prediccions realitzades pels models anteriors. Aquest procés es duu a terme de forma seqüencial. Això vol dir que diversos models s'entrenen a l'etapa 1 i s'ajusten amb precisió. Les probabilitats pronosticades de cada model de l'etapa 1 s'alimenten com a entrada a tots els models a l'etapa 2. Els models a l'etapa 2 després s'ajusten amb precisió i les sortides corresponents s'alimenten als models a l'etapa 3 i així successivament . Aquest procés es produeix diverses vegades en funció de la quantitat de capes d'apilament que voleu utilitzar.

L'etapa final consisteix en un únic model que ens dóna el resultat final en combinar el resultat de tots els models presents a les capes anteriors.

Sovint, lús de classificadors apilables augmenta la precisió de predicció dun model. Però de cap manera no es pot garantir que l'ús d'apilament augmenti la precisió de la predicció en tot moment! El següent [link] pot contenir informació rellevant (http://rasbt.github.io/mlxtend/user_guide/classifier/StackingClassifier/).

Implementació: Construïu un classificador de tipus stacking usant un Gradient Boosting Classifier (amb 20 arbres de decisió, profunditat màxima de 3 nivells i la seed definida a l'inici del segon exercici) que utilitzi com a atributs les prediccions fetes a el conjunt de tests pels algorismes: arbre de decisió, knn i svm. A continuació, calculeu la precisió del model resultant amb cross-validation al conjunt de test (en aquest cas no tenim conjunt de train, amb la qual cosa es fa directament cross-validation sobre test).

Suggeriment:

  • Usar la funció column_stack de numpy per ajuntar totes les prediccions.

Per a més informació, podeu consultar aquest enllaç:

  • column_stack

Per aquesta part he fet servir la classe StackingClassifier the scikitlearn que et permet fer el que es demana més agilment.

In [402]:
from sklearn.ensemble import StackingClassifier
In [403]:
#final model
grad_boost = GradientBoostingClassifier(n_estimators=20, max_depth=3, random_state=myseed)

# models to stack
dtree = DecisionTreeClassifier(max_depth=3, random_state=myseed)
knn_classifier = KNeighborsClassifier(n_neighbors=2)
gamma=0.07
svm = SVC(gamma=gamma, random_state=myseed)

estimators = [
    ("decision_tree", dtree),
     ("knn_classifier", knn_classifier),
     ("svm", svm)]

# Stacked classifier
stacked_classifier = StackingClassifier(estimators=estimators, final_estimator=grad_boost, passthrough=False)
In [404]:
cross_val_score(stacked_classifier, X_train_smote_scaled, y_train_smote)
Out[404]:
array([0.9972093 , 0.99813953, 1.        , 0.99906977, 0.99906977])
In [406]:
stack_acc = train_and_predict(stacked_classifier)
With model StackingClassifier, we have achieved an accuracy score of 0.876 on the test data.

2.2.4. Cascading¶

El cas de cascading és semblant al de stacking però utilitzant no només les prediccions parcials dels classificadors base, sinó també les dades originals.

Implementació: Construeix un classificador de tipus cascading usant un Gradient Boosting Classifier (amb 20 arbres de decisió, profunditat màxima de 3 nivells i la llavor definida a l'inici del segon exercici) que utilitzi com a atributs les prediccions obtingudes amb els models anteriors al conjunt de test (igual que amb l'stacking), i també les variables originals. Calcula la precisió del model resultant amb cross-validation al conjunt de test.

Suggeriment:

  • Usa el mateix conjunt de dades que a l'exercici anterior però afegeix X_test_scaled.
In [407]:
#final model
grad_boost = GradientBoostingClassifier(n_estimators=20, max_depth=3, random_state=myseed)

# models to stack
dtree = DecisionTreeClassifier(max_depth=3, random_state=myseed)
knn_classifier = KNeighborsClassifier(n_neighbors=2)
gamma=0.07
svm = SVC(gamma=gamma, random_state=myseed)

estimators = [
    ("decision_tree", dtree),
     ("knn_classifier", knn_classifier),
     ("svm", svm)]

# stacked classifier WITH pasthrough of orignal data set to TRUE
stacked_classifier_passthrough = StackingClassifier(estimators=estimators, final_estimator=grad_boost, passthrough=True)
In [408]:
cross_val_score(stacked_classifier_passthrough, X_train_smote_scaled, y_train_smote)
Out[408]:
array([0.99906977, 0.99813953, 0.99906977, 0.99906977, 1.        ])
In [409]:
stack_acc_pt = train_and_predict(stacked_classifier_passthrough)
With model StackingClassifier, we have achieved an accuracy score of 0.876 on the test data.
In [410]:
stack_acc_pt
Out[410]:
{'StackingClassifier': 0.8764705882352941}
In [411]:
# Change its name (key) on the dict
stack_acc_pt['StackingClassifierCascade'] = stack_acc_pt.pop('StackingClassifier')

2.2.5. Anàlisi¶

Has aconseguit millorar la precisió amb la combinació seqüencial de classificadors? Comenta'n els resultats.

In [412]:
comb_dict = {**dtree_acc, **rand_forest_acc, **grad_boost_acc, **stack_acc, **stack_acc_pt}
acc_df = pd.DataFrame(list(comb_dict.items()), columns=['Model', 'Accuracy'])
acc_df
Out[412]:
Model Accuracy
0 DecisionTreeClassifier 0.566667
1 RandomForestClassifier 0.876471
2 GradientBoostingClassifier 0.823529
3 StackingClassifier 0.876471
4 StackingClassifierCascade 0.876471
In [413]:
sns.barplot(x='Model', y='Accuracy', data=acc_df)
plt.xticks(rotation=30)
Out[413]:
([0, 1, 2, 3, 4],
 [Text(0, 0, ''),
  Text(0, 0, ''),
  Text(0, 0, ''),
  Text(0, 0, ''),
  Text(0, 0, '')])
No description has been provided for this image

Els nous models amb Stacking han aconseguit igualar, pero no millorar el nostre RandomForestClassifier.

3. Classificació seqüencial i paral·lela amb xarxes neuronals NNs (2.5 punts)¶

Objectiu:¶

Comprendre i implementar models de xarxes neuronals seqüencials i paral·leles per a la classificació utilitzant el conjunt de dades d'Iris. Aquesta part del PAC proporciona informació sobre diferents arquitectures de xarxes neuronals i la seva aplicació a tasques de classificació. Lelecció entre models seqüencials i paral·lels depèn dels requisits específics i la complexitat del conjunt de dades i la tasca.

Tools i Llibreries:¶

Python Colab o Jupyter Notebook (o qualsevol Python IDE). Recomanació: utilitzar Colab. Llibreries: NumPy, Pandes, Scikit-learn, TensorFlow/Keras

En cas de NO utilitzar Colab: Configurar un entorn virtual (opcional però recomanat)¶

És una bona pràctica fer servir un entorn virtual per als seus projectes de Python per evitar conflictes de dependència. Podeu crear un entorn virtual usant venv (integrat a Python) o comta (si utilitzeu Anaconda).

Using venv:

  1. python -m venv myenv

Per activar l'environment:

Al Windows: myenv\Scripts\activate A macOS i Linux: source myenv/bin/activate

Utilitzant comta:

  1. comta create -n myenv python=3.8

  2. comta activate myenv

Instal·lar TensorFlow¶

Keras s'inclou com a part de TensorFlow, per la qual cosa només cal instal·lar TensorFlow. Córrer:

  1. pip install tensorflow

Aquesta comanda instal·la TensorFlow i Keras. Podeu especificar una versió si cal (per exemple, tensorflow==2.4.0).

Verificació de la instal·lació¶

Després de la instal·lació, podeu verificar que tot és correcte si executeu un script simple de Keras/TensorFlow.

Si veieu el següent missatge "NOM_DE_SU_ARCHIVO.ipynb no és de confiança" en el seu terminal (si està treballant en una Mac). Això fa referència a una característica de seguretat a Jupyter. Els notebooks de Jupyter poden contenir codi executable i, per raons de seguretat, quan s'obre un quadern des d'una font que no és de confiança (com si es descarrega d'Internet o es rep d'una altra persona), Jupyter el marca com a “no fiable ". Això significa que el codi no s'executarà automàticament fins que no hagi revisat el contingut i confiï a la font.

Per confiar i executar el notebook:

  1. Obriu el quadenotebook a Jupyter.
  2. Reviseu el codi per assegurar-vos que sigui segur i feu el que espereu.
  3. Si sabeu que el portàtil és segur, pot marcar-lo com a fiable. En general, això es pot fer a través d'una opció de menú a Jupyter, sovint a "Fitxer" o un menú similar.

Després de marcar el notebook com a fiable, podreu executar el codi que conté sense més advertiments. Recordeu, és important confiar en els portàtils només quan estigui segur de la seva seguretat i origen, per evitar executar codi potencialment nociu.

3.1. Entorn de configuració¶

Primer, assegureu-vos que totes les libraries necessàries estiguin instal·lades. Podeu instal·lar-los usant pip (instal·lador de paquets de Python).

In [ ]:
# !pip install numpy pandas scikit-learn tensorflow keras

Després carregueu les libraries:

In [414]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
from keras.models import Model
from keras.layers import Input, Dense, concatenate
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import Input, concatenate
from tensorflow.keras.models import Model

3.2 Codificación One-Hot de la variable objetivo:¶

Codifiqueu les etiquetes categòriques si cal. Si cal, utilitzeu y_train_balanced, y_test. Marqueu-les amb les etiquetes següents: y_train_balanced_encoded i y_test_encoded.

Solución:
In [415]:
# y_train_balanced and y_test are integer class labels
y_train_balanced_encoded = to_categorical(y_train_smote)
y_test_encoded = to_categorical(y_test)

3.3. Model de xarxa neuronal seqüencial (1 pt)¶

Creeu i entreneu un model de xarxa neuronal seqüencial.

Arquitectura model:¶

Implementació: Ha de construir un model seqüencial que anomenarà model_seq amb tres capes: dues capes ocultes i una capa de sortida.

Capa d'entrada: la mida de la capa d'entrada està determinada per X_train_balanced_scaled.shape[1], que correspon a la quantitat d'entrades al conjunt de dades. Aquesta capa no fa cap càlcul.

Primera Capa Oculta: Consta de 64 neurones, utilitzant la funció dactivació ReLU (Unitat Lineal Rectificada). ReLU s'usa comunament en xarxes neuronals perquè introdueix no linealitat sense afectar significativament els gradients, cosa que ajuda durant la retropropagació.

Segona capa oculta: semblant a la primera, aquesta capa també té 64 neurones amb activació ReLU. Tenir múltiples capes de neurones permet a la xarxa aprendre patrons més complexos a les dades.

Capa de sortida: on la quantitat de neurones correspon a y_train_balanced_encoded.shape[1], que és la quantitat de classes de l'outcome. Aquesta capa ha dutilitzar la funció dactivació softmax, la qual cosa fa que la xarxa sigui adequada per a la classificació de classes múltiples.

Compilació de models:¶

El model s'ha de compilar amb l'optimitzador Adam. Adam és una opció popular per a moltes tasques de xarxes neuronals, ja que combina els avantatges de dues extensions més del descens de gradient estocàstic, específicament l'algorisme de gradient adaptatiu (AdaGrad) i la propagació quadràtica mitjana (RMSProp). categorical_crossentropy hauria de ser la funció de pèrdua, que és estàndard per a problemes de classificació de classes múltiples.

Entrenant el model:¶

El model s'entrenarà durant 50 èpoques (epochs) amb una mida de lot de 10 (batch_size). El paràmetre detallat = 1 (verbose) en ajustament mostrarà una barra de progrés durant l'entrenament, cosa que pot ser útil per monitoritzar el procés d'entrenament.

In [416]:
from tensorflow.keras.optimizers import Adam

from tensorflow.keras.utils import plot_model
In [417]:
# Definir el model seqüencial
model_seq = Sequential()

# Capa d'entrada
input_dim = X_train_smote_scaled.shape[1]

# Primera capa oculta
model_seq.add(Dense(64, activation='relu', input_dim=input_dim))

# Segona capa oculta
model_seq.add(Dense(64, activation='relu'))

# Capa de sortida
output_dim = y_train_balanced_encoded.shape[1]
model_seq.add(Dense(output_dim, activation='softmax'))

# Compilar el model
model_seq.compile(optimizer=Adam(), loss='categorical_crossentropy', metrics=['accuracy'])
In [418]:
model_seq.summary()
Model: "sequential_4"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 dense_91 (Dense)            (None, 64)                6656      
                                                                 
 dense_92 (Dense)            (None, 64)                4160      
                                                                 
 dense_93 (Dense)            (None, 5)                 325       
                                                                 
=================================================================
Total params: 11141 (43.52 KB)
Trainable params: 11141 (43.52 KB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
In [419]:
plot_model(model_seq)
Out[419]:
No description has been provided for this image

Normalment es fa servir un validation split durant l'entrenament, pero com que aqui no s'espeficica no l'hem fet servir. La idea del validation es que les metriques es calculen tambe sobre el validation, que no forma part estrictament de l'entrenament i el model es pot parar d'entrenar quan la validationn score no millora.

In [420]:
# Train
history = model_seq.fit(X_train_smote_scaled, y_train_balanced_encoded, epochs=50, batch_size=10, verbose=1)
Epoch 1/50
538/538 [==============================] - 2s 2ms/step - loss: 0.3730 - accuracy: 0.8856
Epoch 2/50
538/538 [==============================] - 1s 2ms/step - loss: 0.0596 - accuracy: 0.9855
Epoch 3/50
538/538 [==============================] - 1s 2ms/step - loss: 0.0218 - accuracy: 0.9953
Epoch 4/50
538/538 [==============================] - 1s 2ms/step - loss: 0.0091 - accuracy: 0.9989
Epoch 5/50
538/538 [==============================] - 1s 2ms/step - loss: 0.0039 - accuracy: 0.9996
Epoch 6/50
538/538 [==============================] - 1s 2ms/step - loss: 0.0016 - accuracy: 1.0000
Epoch 7/50
538/538 [==============================] - 1s 2ms/step - loss: 8.3265e-04 - accuracy: 1.0000
Epoch 8/50
538/538 [==============================] - 1s 2ms/step - loss: 5.0491e-04 - accuracy: 1.0000
Epoch 9/50
538/538 [==============================] - 1s 2ms/step - loss: 3.2983e-04 - accuracy: 1.0000
Epoch 10/50
538/538 [==============================] - 1s 2ms/step - loss: 2.2278e-04 - accuracy: 1.0000
Epoch 11/50
538/538 [==============================] - 1s 2ms/step - loss: 1.5219e-04 - accuracy: 1.0000
Epoch 12/50
538/538 [==============================] - 1s 2ms/step - loss: 1.0734e-04 - accuracy: 1.0000
Epoch 13/50
538/538 [==============================] - 1s 2ms/step - loss: 7.5852e-05 - accuracy: 1.0000
Epoch 14/50
538/538 [==============================] - 1s 2ms/step - loss: 5.4631e-05 - accuracy: 1.0000
Epoch 15/50
538/538 [==============================] - 1s 1ms/step - loss: 3.8788e-05 - accuracy: 1.0000
Epoch 16/50
538/538 [==============================] - 1s 1ms/step - loss: 2.8099e-05 - accuracy: 1.0000
Epoch 17/50
538/538 [==============================] - 1s 2ms/step - loss: 1.9902e-05 - accuracy: 1.0000
Epoch 18/50
538/538 [==============================] - 1s 2ms/step - loss: 1.4556e-05 - accuracy: 1.0000
Epoch 19/50
538/538 [==============================] - 1s 2ms/step - loss: 1.0615e-05 - accuracy: 1.0000
Epoch 20/50
538/538 [==============================] - 1s 2ms/step - loss: 7.6223e-06 - accuracy: 1.0000
Epoch 21/50
538/538 [==============================] - 1s 2ms/step - loss: 5.5650e-06 - accuracy: 1.0000
Epoch 22/50
538/538 [==============================] - 1s 2ms/step - loss: 4.0371e-06 - accuracy: 1.0000
Epoch 23/50
538/538 [==============================] - 1s 2ms/step - loss: 2.9962e-06 - accuracy: 1.0000
Epoch 24/50
538/538 [==============================] - 1s 2ms/step - loss: 2.1669e-06 - accuracy: 1.0000
Epoch 25/50
538/538 [==============================] - 1s 2ms/step - loss: 1.6033e-06 - accuracy: 1.0000
Epoch 26/50
538/538 [==============================] - 1s 2ms/step - loss: 1.1786e-06 - accuracy: 1.0000
Epoch 27/50
538/538 [==============================] - 1s 2ms/step - loss: 8.7157e-07 - accuracy: 1.0000
Epoch 28/50
538/538 [==============================] - 1s 2ms/step - loss: 6.3113e-07 - accuracy: 1.0000
Epoch 29/50
538/538 [==============================] - 1s 2ms/step - loss: 4.6530e-07 - accuracy: 1.0000
Epoch 30/50
538/538 [==============================] - 1s 2ms/step - loss: 3.4346e-07 - accuracy: 1.0000
Epoch 31/50
538/538 [==============================] - 1s 2ms/step - loss: 2.5193e-07 - accuracy: 1.0000
Epoch 32/50
538/538 [==============================] - 1s 2ms/step - loss: 1.8712e-07 - accuracy: 1.0000
Epoch 33/50
538/538 [==============================] - 1s 2ms/step - loss: 1.3666e-07 - accuracy: 1.0000
Epoch 34/50
538/538 [==============================] - 1s 1ms/step - loss: 1.0089e-07 - accuracy: 1.0000
Epoch 35/50
538/538 [==============================] - 1s 2ms/step - loss: 7.4054e-08 - accuracy: 1.0000
Epoch 36/50
538/538 [==============================] - 1s 2ms/step - loss: 5.4115e-08 - accuracy: 1.0000
Epoch 37/50
538/538 [==============================] - 1s 2ms/step - loss: 4.0365e-08 - accuracy: 1.0000
Epoch 38/50
538/538 [==============================] - 1s 1ms/step - loss: 2.9586e-08 - accuracy: 1.0000
Epoch 39/50
538/538 [==============================] - 1s 1ms/step - loss: 2.2378e-08 - accuracy: 1.0000
Epoch 40/50
538/538 [==============================] - 1s 1ms/step - loss: 1.6523e-08 - accuracy: 1.0000
Epoch 41/50
538/538 [==============================] - 1s 1ms/step - loss: 1.2220e-08 - accuracy: 1.0000
Epoch 42/50
538/538 [==============================] - 1s 1ms/step - loss: 9.1154e-09 - accuracy: 1.0000
Epoch 43/50
538/538 [==============================] - 1s 2ms/step - loss: 6.8310e-09 - accuracy: 1.0000
Epoch 44/50
538/538 [==============================] - 1s 2ms/step - loss: 4.9458e-09 - accuracy: 1.0000
Epoch 45/50
538/538 [==============================] - 2s 4ms/step - loss: 4.1252e-09 - accuracy: 1.0000
Epoch 46/50
538/538 [==============================] - 1s 2ms/step - loss: 3.1050e-09 - accuracy: 1.0000
Epoch 47/50
538/538 [==============================] - 1s 2ms/step - loss: 2.1513e-09 - accuracy: 1.0000
Epoch 48/50
538/538 [==============================] - 1s 1ms/step - loss: 1.7299e-09 - accuracy: 1.0000
Epoch 49/50
538/538 [==============================] - 1s 2ms/step - loss: 1.2642e-09 - accuracy: 1.0000
Epoch 50/50
538/538 [==============================] - 1s 2ms/step - loss: 1.0867e-09 - accuracy: 1.0000
In [421]:
def training_plot(history):
    """
    Plot the training loss and accuracy, next to the validation loss and accuracy
    Params: history object from training a keras model
    Return: Fig, axes
    """
    fig, axes = plt.subplots(ncols=2, figsize=(12, 4))
    axes[0].plot(history.history['accuracy'], label='Training')
    #axes[0].plot(history.history['val_accuracy'], label='Validation')
    axes[0].set_title('Accuracy')
    axes[0].legend()
    axes[0].set_ylabel("Accuracy")
    axes[1].plot(history.history['loss'], label='Training')
    #axes[1].plot(history.history['val_loss'], label='Validation')
    axes[1].set_title('Loss')
    axes[1].legend()
    axes[1].set_ylabel("Loss")
    for ax in axes:
        ax.set_xlabel("Epoch")
    plt.show()

    return fig, axes
In [422]:
training_plot(history)
No description has been provided for this image
Out[422]:
(<Figure size 1200x400 with 2 Axes>,
 array([<AxesSubplot:title={'center':'Accuracy'}, xlabel='Epoch', ylabel='Accuracy'>,
        <AxesSubplot:title={'center':'Loss'}, xlabel='Epoch', ylabel='Loss'>],
       dtype=object))
In [424]:
y_pred_prob_seq = model_seq.predict(X_test_scaled)
y_pred_prob_seq
16/16 [==============================] - 0s 1ms/step
Out[424]:
array([[9.9999982e-01, 4.7393687e-08, 1.7201629e-08, 9.6744913e-08,
        1.6019825e-09],
       [9.9999994e-01, 2.6907721e-11, 7.5566484e-20, 3.9805545e-13,
        3.7622987e-17],
       [9.9999994e-01, 2.2662623e-12, 1.2833130e-11, 3.4072207e-11,
        7.1539598e-16],
       ...,
       [1.0000000e+00, 2.8214384e-11, 4.7209046e-18, 2.7665483e-16,
        1.9700219e-20],
       [1.0000000e+00, 6.4241577e-11, 4.3006118e-19, 1.2123955e-19,
        2.6655768e-20],
       [9.9840063e-01, 5.0945108e-08, 3.0140429e-06, 1.1070739e-15,
        1.5963641e-03]], dtype=float32)
In [425]:
y_pred_seq = np.argmax(y_pred_prob_seq, axis=1)
y_pred_seq
Out[425]:
array([0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       1, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 3,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
       0, 0, 0, 0])
In [426]:
# ground truth vs pred 
plt.scatter(y_test, y_pred_seq)
plt.xlabel("Ground Truth Labels")
plt.ylabel("Predicted Labels")
Out[426]:
Text(0, 0.5, 'Predicted Labels')
No description has been provided for this image
In [427]:
acc_score = accuracy_score(y_pred_seq, y_test)
    
# Extract the name of the model from the class
model_name = str(type(model_seq)).split(".")[-1][:-2]
    
print(f"With model {model_name}, we have achieved an accuracy score of {acc_score:.3f} on the test data.")
With model Sequential, we have achieved an accuracy score of 0.841 on the test data.

3.4. Model de xarxa neuronal paral·lela (1 pt)¶

Descripció detallada del codi:¶

Implementació:

  1. Capes d'entrada:
  • Defineix dues capes de Input, input_a i input_b. Cal calcular la mida de divisió per a les entrades paral·leles prenent la meitat del nombre total de característiques a les dades escalades d'entrenament (X_train_balanced_scaled). Si el nombre total de característiques és imparell, ajusta el split_size sumant-li un per assegurar una distribució equitativa de les característiques entre les dues entrades del model. Després defineix les entrades del model neuronal: input_a rep la primera meitat de les característiques, mentre que input_b rep la segona meitat. Aquesta configuració permetrà que la xarxa processi diferents subconjunts de funcions, que podrien transformar-se o dissenyar-se de manera diferent en futures millores.
  1. Configuració de neurona variable:
  • L'script ha de recórrer de 30 a 71 neurones, amb passos de 10, ajustant la complexitat de la xarxa. Cada iteració ha de construir dos camins paral·lels:
  • Ruta A i Ruta B: Cada ruta conté una capa "Dense" amb el recompte de neurones actual i activació ReLU per a una transformació no lineal, seguida d'una capa "Dropout" per evitar el sobreajustament en deshabilitar aleatòriament el 20 % de les neurones durant l'entrenament.
  1. Concatenació i capes finals:
  • Les sortides de la Ruta A i la Ruta B es fusionaran en una sola anomenada "merged" mitjançant la funció "concatenate". Aquestes dades combinades passen a través d'una altra capa "Dense" amb 64 ​​neurones i activació ReLU, la qual cosa porta a la capa "Dense" de sortida final, anomenada "output" configurada amb neurones igual al nombre de classes i activació softmax per a la classificació .
  1. Compilació del model:
  • El model s'ha de compilar amb l'optimitzador Adam per les seves capacitats de taxa d'aprenentatge adaptatiu i categorical_crossentropy com a funció de pèrdua, adequada per a problemes de classes múltiples. La precisió serà monitoritzada com una mètrica de desenvolupament.
  1. Entrenament:
  • L'entrenament s'executarà utilitzant "fit" en dades balancejades i escalades durant 10 epochs amb un batch_size de 64, equilibrant l'eficiència computacional i la precisió, validation_split de 0.1 i verbose 0.
  1. Avaluació:
  • Després de l'entrenament, cada configuració del model s'ha d'avaluar al conjunt de prova anomenat "scores" per mesurar la precisió i avaluar l'impacte de la variació neuronal en la generalització del model.
  1. Recopilació de resultats:
  • Compila i presenta puntuacions de precisió per a totes les configuracions, brindant-li informació sobre el recompte òptim de neurones per a aquest conjunt de dades i tasca de modelatge. Per a l'Avaluació i Anàlisi només fa servir el model amb millors resultats.
  1. Recalcar les dimensions de les dues particions del model en paral·lel
  • Vigila a cada apartat on calgui utilitzar els resultats d'aquest model s'han d'adequar les dimensions de la manera següent:
# Calculate the correct split size to ensure even feature distribution for the parallel inputs
split_size = X_test_scaled.shape[1] // 2
if X_test_scaled.shape[1] % 2 != 0:  # Adjust if the total number of features is odd
   split_size += 1
In [428]:
from tensorflow.keras.layers import Input, Dense, Dropout, concatenate
In [429]:
def train_parallel_model(n_neurons):
    split_size = X_train_smote_scaled.shape[1] // 2

    if X_train_smote_scaled.shape[1] % 2 != 0:
        split_size += 1

    # Definir les capes d'entrada
    input_a = Input(shape=(split_size,))
    input_b = Input(shape=(X_train_smote_scaled.shape[1] - split_size,))

    # Ruta A
    x = Dense(n_neurons, activation='relu')(input_a)
    x = Dropout(0.2)(x)

    # Ruta B
    y = Dense(n_neurons, activation='relu')(input_b)
    y = Dropout(0.2)(y)

    # Concatenació i capes finals
    merged = concatenate([x, y])
    z = Dense(64, activation='relu')(merged)
    output = Dense(y_train_balanced_encoded.shape[1], activation='softmax')(z)

    # Crear el model
    model = Model(inputs=[input_a, input_b], outputs=output)

    # Compilar el model
    model.compile(optimizer=Adam(), loss='categorical_crossentropy', metrics=['accuracy'])
    
    # Entrenar el model
    history = model.fit([X_train_smote_scaled[:, :split_size], X_train_smote_scaled[:, split_size:]],
                        y_train_balanced_encoded, epochs=10, batch_size=64, validation_split=0.1, verbose=0)

    # Avaluar el model
    y_pred_prob = model.predict([X_test_scaled[:, :split_size], X_test_scaled[:, split_size:]])
    y_pred = np.argmax(y_pred_prob, axis=1)

    acc_score = accuracy_score(y_test, y_pred)
    
    return model, y_pred, acc_score
In [430]:
results = {}
for n_neurons in range(30, 71, 10):
    model, y_pred, acc_score = train_parallel_model(n_neurons)
    results[n_neurons] = acc_score
    print(f"With {n_neurons} neurons, achieved accuracy: {acc_score:.3f}")
16/16 [==============================] - 0s 2ms/step
With 30 neurons, achieved accuracy: 0.747
16/16 [==============================] - 0s 2ms/step
With 40 neurons, achieved accuracy: 0.818
16/16 [==============================] - 0s 2ms/step
With 50 neurons, achieved accuracy: 0.822
16/16 [==============================] - 0s 1ms/step
With 60 neurons, achieved accuracy: 0.829
16/16 [==============================] - 0s 1ms/step
With 70 neurons, achieved accuracy: 0.829
In [432]:
model, y_pred_parallel, acc_score = train_parallel_model(70)
16/16 [==============================] - 0s 2ms/step
In [433]:
acc_score = accuracy_score(y_test,y_pred_parallel)
print(f"With model parallel, we have achieved an accuracy score of {acc_score:.3f} on the test data.")
With model parallel, we have achieved an accuracy score of 0.831 on the test data.

3.5. Avaluació i anàlisi (0.5 punts)¶

Avalueu (avalua't) tots dos models en el conjunt de dades de prova i imprimiu en pantalla l'exactitud (accuracy) per a cadascun.

En el model sequencial hem obtingut una accuracy_score bastant baixa, de 0.6 en el test data. En el model parallel hem obtingut 0.847. Malgrat tot, el millor model segueix sent el RandomForest.

Anàlisi¶

Hi ha diverses maneres de presentar els resultats de l'avaluació dels models, especialment per a tasques de classificació. Dos mètodes comunament utilitzats són la matriu de confusió i les corbes de característica operativa del receptor (ROC). Aquests mètodes ens brinden una millor comprensió del rendiment del model més enllà de la precisió.

Matriu de confusió: una matriu de confusió és una taula que sutilitza sovint per descriure el rendiment dun model de classificació en un conjunt de dades de prova. Ajuda a visualitzar el rendiment dun algorisme.

Corba ROC i AUC. La corba ROC és una representació gràfica de la capacitat de diagnòstic dun sistema classificador binari. Per a la classificació de classes múltiples, les corbes ROC es poden estendre per traçar les corbes ROC micromitjana i macropromig.

Compareu el rendiment dels models seqüencial i paral·lel. Analitzeu les fortaleses i debilitats de cada enfocament.

In [436]:
# Function to visualize the confusion matrix
def plot_confusion_matrix(cm, class_names, fmt=".2f"):
    """
    Plot the confusion matrix calculated from sklearn.confusion_matrix()

    """
    plt.figure(figsize=(10, 10))

    sns.heatmap(cm, annot=True, fmt=fmt, cmap='Blues', xticklabels=class_names, yticklabels=class_names)
    plt.xlabel('Predicted label')
    plt.ylabel('True label')
    plt.show()
Sequential¶
In [437]:
# Compute confusion matrix

cm = confusion_matrix(y_test,y_pred_seq)

# Compute the normalized confusion matrix

cm_norm = confusion_matrix(y_test,y_pred_seq, normalize="true")
In [438]:
class_names = ['Class0', 'Class1', 'Class2', 'Class3', 'Class4']
In [439]:
plot_confusion_matrix(cm_norm, class_names, fmt=".2f")
No description has been provided for this image
In [440]:
cm = confusion_matrix(y_test,y_pred_parallel)

# Compute the normalized confusion matrix

cm_norm = confusion_matrix(y_test,y_pred_parallel, normalize="true")
In [441]:
plot_confusion_matrix(cm_norm, class_names, fmt=".2f")
No description has been provided for this image

4. Conclusions (1 punt)¶

Anàlisi:

Feu lelecció de la millor eina de classificació per al problema de miocardi. Tingueu en compte les característiques específiques del conjunt de dades i les mètriques de rendiment, com ara l'exactitud, la precisió, etc.

Jo escolliria el random forest classifier perquè es el model que ha donat la millor score, juntament amb els stacked. El Random forest però es mes senzill i facil d'entrenar que els stacked models. No triaria les xarxes neuronals perque son models mes "black box" on costa interpretar les prediccions. No entenc ben bé perque les NN han fet la majoria de prediccions a la classe 0, perquè precisament això era el que es volia evitar amb l'SMOTE. He revisat que el codi estigués correcte i efectivament s;hagues executat amb els datasets balanced i no he trobat cap error.

In [ ]: